#ifndef __XDO_CRYPTO_A__
#define __XDO_CRYPTO_A__

#include "../util/utils.pe.h"

namespace xm
{
	class XdoCryptoA : public IKDetector, UnkBase
	{
	public:
		UNKNOWN_IMP1(IKDetector);
		DEFAULT_INIT_CLASS();
		STDMETHOD(Detect)( IKDetectSession * sess, DetectResult * pdr )
		{
			iptr<IKBStream> stm( sess->TargetStream() );
			if( !stm ) return E_FAIL;

			if( stm->Length() > 2*1024*1024 )
				return E_FAIL;

			iptr<IPeReader> winpe( sess->CurrentArchive() );
			if( !winpe ) return E_FAIL;

			if( winpe->IsWow64() ) return E_FAIL;

			UINT32 uCase = FALSE;
			RFAILED( TestCompilerSectionTable( winpe, uCase ) );
			RASSERT( !uCase, E_FAIL );

			DWORD dwInfMask = 0, iEntrySectId = 0;
			RFAILED( TestInfectOrPacked( winpe, dwInfMask, &iEntrySectId ) );

			if( dwInfMask ) return E_FAIL;
			if( iEntrySectId == (DWORD)-1 ) return E_FAIL;

			DWORD dwLibCount = 0, dWFuncCount = 0;
			if( !winpe->ImportCount( &dwLibCount, &dWFuncCount ) ) return E_FAIL;
			if( dwLibCount < 3 || dwLibCount > 12 ) return E_FAIL;
			if( dWFuncCount < 16 || dWFuncCount > 120 ) return E_FAIL;

			iptr<IKPETools> petool;
			RFAILED( get_this_pe_tools( sess, winpe, petool.pp() ) );

			iptr<IKTrunkFetcher> trunk;
			RFAILED( get_this_pe_trunk_fetcher( sess, petool, trunk.pp() ) );

			RFAILED( trunk->Fetch( PE_TRUNK_API ) );

			std::set<size_t> uniq_set;

			size_t allc = trunk->GetCount();
			for( size_t i = 0; i < allc; ++ i )
			{
				TRUNK_ITEM item = {};
				RFAILED( trunk->GetItem( i, &item ) );
				if( item.kls == PE_TRUNK_API )
					uniq_set.insert( item.uniq );
			}

			double used = uniq_set.size();
			double alli = dWFuncCount;

			double rate = used/alli;

			if( rate < 0.7 )
			{
				if( rate < 0.4 )
					pdr->Level = DLEV_MALW_EXACT;
				else
					pdr->Level = DLEV_MALW_HEUR;
				//MessageBoxA(0,0,0,0);
				return S_OK;
			}

			return E_FAIL;
		}
		STDMETHOD(Treat)( IKDetectSession * ctx, DetectResult * pdr )
		{
			return E_FAIL;
		}
		STDMETHOD(Reset)()
		{
			return S_OK;
		}
	};

	class XdoCryptoB : public IKDetector, UnkBase
	{
	public:
		UNKNOWN_IMP1(IKDetector);
		DEFAULT_INIT_CLASS();
		STDMETHOD(Detect)( IKDetectSession * sess, DetectResult * pdr )
		{
			iptr<IKBStream> stm( sess->TargetStream() );
			if( !stm ) return E_FAIL;

			if( stm->Length() > 2*1024*1024 )
				return E_FAIL;

			iptr<IPeReader> winpe( sess->CurrentArchive() );
			if( !winpe ) return E_FAIL;

			if( winpe->IsWow64() ) return E_FAIL;

			UINT32 uCase = FALSE;
			RFAILED( TestCompilerSectionTable( winpe, uCase ) );
			RASSERT( !uCase, E_FAIL );

			DWORD dwInfMask = 0, iEntrySectId = 0;
			RFAILED( TestInfectOrPacked( winpe, dwInfMask, &iEntrySectId ) );

			if( dwInfMask ) return E_FAIL;
			if( iEntrySectId == (DWORD)-1 ) return E_FAIL;

			DWORD dwLibCount = 0, dWFuncCount = 0;
			if( !winpe->ImportCount( &dwLibCount, &dWFuncCount ) ) return E_FAIL;
			if( dwLibCount < 2 || dwLibCount > 5 ) return E_FAIL;
			if( dWFuncCount < 14 || dWFuncCount > 40 ) return E_FAIL;

			iptr<IKPETools> petool;
			RFAILED( get_this_pe_tools( sess, winpe, petool.pp() ) );

			if( !petool->IsMoudleImported("kernel32", KeyStrInModuleName) )
				return E_FAIL;

			LPCSTR aSqlwid = petool->IsMoudleImported("sqlwid", KeyStrInModuleName);
			if( !aSqlwid )
				return E_FAIL;

			if( !winpe->IsImportFunction( aSqlwid, "strerror_" ) )
				return E_FAIL;

			iptr<IKTrunkFetcher> trunk;
			RFAILED( get_this_pe_trunk_fetcher( sess, petool, trunk.pp() ) );

			RFAILED( trunk->Fetch( PE_TRUNK_API ) );

			size_t callc = 0, sepc_api_call = 0;
			size_t allc = trunk->GetCount();
			for( size_t i = 0; i < allc; ++ i )
			{
				TRUNK_ITEM item = {};
				RFAILED( trunk->GetItem( i, &item ) );
				if( item.kls == PE_TRUNK_API )
					callc ++;
				if( !tcs::cmp( item.txt, "strerror_" ) )
					sepc_api_call ++;
			}

			if( callc > 35 || !sepc_api_call ) 
				return E_FAIL;

			return S_OK;
		}
		STDMETHOD(Treat)( IKDetectSession * ctx, DetectResult * pdr )
		{
			return E_FAIL;
		}
		STDMETHOD(Reset)()
		{
			return S_OK;
		}
	};


	struct DENY_API
	{
		LPCSTR aApiName;
		SIZE_T iArgc;
	};

	static const DENY_API XdoObfusA_APIs [] = {
		
		{ "CreateFileA", 7 },
		{ "GetProcAddress", 2 },
		{ 0, 0 }
	};


	class XdoObfusA : public IKDetector, UnkBase
	{
	public:
		UNKNOWN_IMP1(IKDetector);
		DEFAULT_INIT_CLASS();
		STDMETHOD(Detect)( IKDetectSession * sess, DetectResult * pdr )
		{
			pdr->Level = DLEV_MALW_EXACT;

			iptr<IKBStream> stm( sess->TargetStream() );
			if( !stm ) return E_FAIL;

			if( stm->Length() > 2*1024*1024 )
				return E_FAIL;

			iptr<IPeReader> winpe( sess->CurrentArchive() );
			if( !winpe ) return E_FAIL;

			if( winpe->IsWow64() ) return E_FAIL;

			UINT32 uCase = FALSE;
			RFAILED( TestCompilerSectionTable( winpe, uCase ) );
			RASSERT( !uCase, E_FAIL );

			DWORD dwInfMask = 0, iEntrySectId = 0;
			RFAILED( TestInfectOrPacked( winpe, dwInfMask, &iEntrySectId ) );

			if( dwInfMask ) return E_FAIL;
			if( iEntrySectId == (DWORD)-1 ) return E_FAIL;

			DWORD dwLibCount = 0, dWFuncCount = 0;
			if( !winpe->ImportCount( &dwLibCount, &dWFuncCount ) ) return E_FAIL;
			if( dwLibCount < 3 || dwLibCount > 12 ) return E_FAIL;
			if( dWFuncCount < 16 || dWFuncCount > 150 ) return E_FAIL;

			iptr<IKPETools> petool;
			RFAILED( get_this_pe_tools( sess, winpe, petool.pp() ) );

			iptr<IKTrunkFetcher> trunk;
			RFAILED( get_this_pe_trunk_fetcher( sess, petool, trunk.pp() ) );

			RFAILED( trunk->Fetch( PE_TRUNK_API ) );

			std::set<size_t> uniq_set;
			UINT8 buffer[64] = {};

			size_t EqualRgn_c = 0;

			size_t allc = trunk->GetCount();
			for( size_t i = 0; i < allc; ++ i )
			{
				TRUNK_ITEM item = {};
				RFAILED( trunk->GetItem( i, &item ) );
				
				if( !winpe->ReadDatRaw( item.fpos - 2, buffer, 2 ) )
					continue;

				if( buffer[0] != 0xFF || buffer[1] != 0x15 ) 
					continue;

				if( !strcmp( item.txt, "EqualRgn" ) )
				{
					if( !winpe->ReadDatRaw( item.fpos - 7, buffer, 7 ) )
						continue;
					if( !memcmp( buffer, "\x68\xEC\x6B\x69\x7B", 5 ) )
						return S_OK;
				}

				for( size_t i = 0; XdoObfusA_APIs[i].aApiName; ++ i )
				{
					const DENY_API & dapi = XdoObfusA_APIs[i];
					size_t argc = dapi.iArgc;
					if( argc >= sizeof(buffer)/2 ) continue;

					if( strcmp( item.txt, dapi.aApiName ) )
						continue;

					size_t sub_dist = 2 + 2 * argc;
					if( !winpe->ReadDatRaw( item.fpos - sub_dist, buffer, sub_dist - 2 ) )
						continue;

					size_t kk = 0; argc <<= 1;
					for( ; kk < argc; kk += 2 )
					{
						if( buffer[kk] != 0x6A || buffer[kk+1] != 0x00 )
							break;
					}

					if( kk == argc ) 
						return S_OK;
				}
			}
			return E_FAIL;
		}
		STDMETHOD(Treat)( IKDetectSession * ctx, DetectResult * pdr )
		{
			return ctx->RawStream()->Remove();
		}
		STDMETHOD(Reset)()
		{
			return S_OK;
		}
	};

	//namespace libemu
	//{
	//	enum RegIdx
	//	{
	//		I_EAX, I_ECX, I_EDX, I_EBX,
	//		I_ESP, I_EBP, I_ESI, I_EDI,
	//		I_EIP, I_EFL, 

	//		I_CS, I_DS, I_ES, I_FS, I_GS, I_SS,
	//	};

	//	struct Context32
	//	{
	//		uint32_t regs[8];
	//		uint32_t eip;
	//		uint32_t efl;
	//	};

	//	enum 
	//	{
	//		ACC_READ	= 0x1,
	//		ACC_WRITE	= 0x2,
	//		ACC_EXEC	= 0x4,
	//		PGF_GRIT	= 0x1000,		// PAGE GRIT Access, 
	//		PGF_MOA		= 0x2000,		// PAGE MAP ON ACCESS
	//	};

	//	class IEmuMemPrivoder;

	//	struct EMU_MEM_PAGE
	//	{
	//		LPVOID		HostPtr;
	//		SIZE_T		Paga;
	//		SIZE_T		UserData;
	//	};

	//	class IEmuMem : public IUnknown
	//	{
	//	public:
	//		STDMETHOD(SetCatchHandler)( IEmuMemPrivoder * provider ) PURE;
	//		STDMETHOD(MapPage)( SIZE_T guest, EMU_MEM_PAGE * lpPageInfo ) PURE;	// IF HOST == 0, PGF_GRIF|PGF_MOA must be set
	//		STDMETHOD(Unmap)( SIZE_T guest ) PURE;
	//		STDMETHOD(QueryPage)( SIZE_T guest, EMU_MEM_PAGE * lpPageInfo ) PURE;
	//	};

	//	class IEmuMemPrivoder
	//	{
	//	public:
	//		virtual long	ImplGritAccess( IEmuMem * lpMem, uint32_t guest, size_t bytes, uint32_t acc, EMU_MEM_PAGE * lpPageInfo, void * outbuf ) = 0;
	//		virtual void*	ImplMapOnAccess( IEmuMem * lpMem, uint32_t guest, uint32_t acc, EMU_MEM_PAGE * lpPageInfo ) = 0;
	//	};


	//	class IEmuCpu : public IUnknown
	//	{
	//	public:
	//		STDMETHOD(SetContext)( Context32 * ctx ) PURE;
	//		STDMETHOD(GetContext)( Context32 * ctx ) PURE;
	//		STDMETHOD(SetRegister)( RegIdx idx, uint32_t val ) PURE;
	//		STDMETHOD(GetRegister)( RegIdx idx, uint32_t * val ) PURE;
	//		STDMETHOD(SetSegment)( RegIdx idx, SIZE_T base, SIZE_T limit ) PURE;
	//		STDMETHOD(QuerySegement)( RegIdx idx, SIZE_T* base, SIZE_T* limit ) PURE;
	//		STDMETHOD(Execute)( SIZE_T steps, SIZE_T * executed ) = 0;
	//		STDMETHOD(ExecuteTo)( uint32_t eip, SIZE_T * executed = 0 ) PURE;
	//		STDMETHOD_(int,GetError)() PURE;
	//	};

	//	class IEmuModuleQuery
	//	{
	//	public:
	//		virtual SIZE_T _get_module_handle( LPCSTR lpModule ) PURE;
	//		virtual SIZE_T _get_proc_address( SIZE_T hModule, LPCSTR aProcName ) PURE;
	//	};

	//	class IEmuProcess : public IEmuMem
	//	{
	//	public:
	//		STDMETHOD(LoadImage)( IPeReader * pef, SIZE_T flags, IEmuModuleQuery * helper ) = 0;
	//		STDMETHOD(ReadMemory)( SIZE_T guest, PVOID buffer, SIZE_T bytes, SIZE_T * eff ) = 0;
	//		STDMETHOD(WriteMemory)( SIZE_T guest, LPCVOID buffer, SIZE_T bytes, SIZE_T * eff ) = 0;
	//	};

	//	class IEmuThread0 : public IEmuCpu
	//	{
	//	public:
	//		STDMETHOD(BindProcess)( IEmuProcess * proc ) PURE;
	//		STDMETHOD(SetTebAddress)( SIZE_T lpAddress ) PURE;
	//	};
	//};

	//class VirutVM : public IEmuModuleQuery, public IEmuMemPrivoder
	//{
	//public:

	//	enum 
	//	{
	//		UD_BIT_CODE_PAGE,
	//	};

	//	virtual SIZE_T _get_module_handle( LPCSTR lpModule )
	//	{
	//		if( tcs::nicmp( lpModule, "kernel32", 8 ) )
	//			return 0x70000000;
	//		return 0x7C800000;
	//	}
	//	virtual SIZE_T _get_proc_address( SIZE_T hModule, LPCSTR aProcName )
	//	{
	//		if( hModule == 0x7C800000 ) return 0x7C801000;
	//		return hModule;
	//	}

	//	virtual long	ImplGritAccess( IEmuMem * lpMem, uint32_t guest, size_t bytes, uint32_t acc, EMU_MEM_PAGE * lpPageInfo, void * outbuf )
	//	{
	//		if( acc == ACC_READ )
	//		{
	//			if( guest == 0x7C800000 ) 
	//			{
	//				if( bytes <= 4 ) memcpy( outbuf, "MZ\x90\x00", bytes );
	//				else return EINVAL;
	//			}
	//			else if( guest == 0x7C800030 )
	//			{
	//				if( bytes <= 4 ) *(uint32_t*)outbuf = 0xF0;
	//				else return EINVAL;
	//			}
	//		}
	//		else if( acc == ACC_WRITE )
	//		{
	//			if( lpPageInfo->UserData & UD_BIT_CODE_PAGE )
	//			{
	//				
	//			}
	//			else return EINVAL;
	//		}
	//	}
	//	virtual long	ImplMapOnAccess( IEmuMem * lpMem, uint32_t guest, EMU_MEM_PAGE * lpPageInfo )
	//	{
	//		lpPageInfo->HostPtr;
	//		return 0;
	//	}
	//};

	//class XdoVirutCe : public IKDetector, UnkBase, 
	//{
	//public:
	//	UNKNOWN_IMP1(IKDetector);
	//	DEFAULT_INIT_CLASS();
	//	STDMETHOD(Detect)( IKDetectSession * sess, DetectResult * pdr )
	//	{
	//		iptr<libemu::IEmuProcess> proc;
	//		iptr<IPeReader> winpe( sess->CurrentArchive() );
	//		if( !winpe ) return E_FAIL;
	//		RFAILED( proc->LoadImage( winpe, 0, this ) );
	//		//////////////////////////////////////////////////////////////////////////
	//		EMU_MEM_PAGE StackPage;
	//		StackPage.HostPtr = 0;
	//		StackPage.Paga = ACC_READ|ACC_WRITE|PGF_MOA;
	//		StackPage.UserData = 0;
	//		//////////////////////////////////////////////////////////////////////////
	//		// alloc stack
	//		for( size_t i = 0x00200000; i <0x00210000; i += 0x1000 )
	//			proc->MapPage( 0x00200000, &StackPage );
	//		//////////////////////////////////////////////////////////////////////////
	//		// map EXPORT TABLE PAGES os kernel32.dll to Process
	//		//////////////////////////////////////////////////////////////////////////
	//		iptr<libemu::IEmuThread0> tho;
	//		RFAILED( tho->SetRegister( libemu::I_EIP, 0x12312312 ) );
	//		RFAILED( tho->SetRegister( libemu::I_ESP, 0x00210000 ) );
	//		RFAILED( tho->SetSegment( I_FS, 0x7FFD0000, 0x1000 ) );
	//		RFAILED( tho->SetTebAddress( 0x7FFD0000 ) );
	//		RFAILED( tho->Execute( 1000 ) );
	//		//////////////////////////////////////////////////////////////////////////
	//		int err = tho->GetError();
	//		if( err != ECANCELED ) return E_FAIL;
	//		//////////////////////////////////////////////////////////////////////////
	//	}

	//	virtual long access_memory( uint32_t guest, size_t bytes, uint32_t acc, void * host, void * outbuf )
	//	{
	//		// TEB Read
	//		if( guest == 0x7FFD0018 && bytes == 4 ) 
	//		{
	//			*(uint32_t*)outbuf = 0x7FFD0000;
	//		}
	//		//////////////////////////////////////////////////////////////////////////
	//		// kernel32.dll access
	//		if( guest == 0x7C800000 ) 
	//		{
	//			memcpy( outbuf, "MZ", MIN(bytes,2) );
	//			*(uint32_t*)outbuf = 0x7FFD0000;
	//		}
	//		else if( guest == 0x7C800030 )
	//		{
	//			// read DOS+0x3C
	//		}
	//		else if( acc & in_last_section() )
	//		{
	//			
	//		}
	//	}

	//};


};


#endif